/*
 * Asterisk -- An open source telephony toolkit.
 *
 * Copyright (C) 1999 - 2005, Digium, Inc.
 *
 * Mark Spencer <markster@digium.com>
 *
 * Goertzel routines are borrowed from Steve Underwood's tremendous work on the
 * DTMF detector.
 *
 * See http://www.asterisk.org for more information about
 * the Asterisk project. Please do not directly contact
 * any of the maintainers of this project for assistance;
 * the project provides a web site, mailing lists and IRC
 * channels for your use.
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License Version 2. See the LICENSE file
 * at the top of the source tree.
 */

/*! \file
 *
 * \brief Convenience Signal Processing routines
 *
 * \author Mark Spencer <markster@digium.com>
 * \author Steve Underwood <steveu@coppice.org>
 */

/*! \li \ref dsp.c uses the configuration file \ref dsp.conf
 * \addtogroup configuration_file Configuration Files
 */

/*!
 * \page dsp.conf dsp.conf
 * \verbinclude dsp.conf.sample
 */

/* Some routines from tone_detect.c by Steven Underwood as published under the zapata library */
/*
	tone_detect.c - General telephony tone detection, and specific
					detection of DTMF.

	Copyright (C) 2001  Steve Underwood <steveu@coppice.org>

	Despite my general liking of the GPL, I place this code in the
	public domain for the benefit of all mankind - even the slimy
	ones who might try to proprietize my work and use it to my
	detriment.
*/

/*** MODULEINFO
	<support_level>core</support_level>
 ***/


#include <math.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <syslog.h>
#include <stdarg.h>
#include <string.h>
#include "jitterbuffer/asterisk/frame.h"

#include "dsp.h"
#include "voipmonitor_define.h"

#define ARRAY_LEN(a) (size_t) (sizeof(a) / sizeof(0[a]))

int dspdebug = 0;

/*
#include "asterisk/format_cache.h"
#include "asterisk/channel.h"
#include "asterisk/dsp.h"
#include "asterisk/ulaw.h"
#include "asterisk/alaw.h"
#include "asterisk/utils.h"
#include "asterisk/options.h"
#include "asterisk/config.h"
*/

static struct progalias {
	const char *name;
	enum prog_mode mode;
} aliases[] = {
	{ "us", PROG_MODE_NA },
	{ "ca", PROG_MODE_NA },
	{ "cr", PROG_MODE_CR },
	{ "br", PROG_MODE_CR },
	{ "uk", PROG_MODE_UK },
};

static struct progress {
	enum gsamp_size size;
	int freqs[10];
} modes[] = {
	{ GSAMP_SIZE_NA, { 350, 400, 425, 440, 450, 480, 620, 950, 1400, 1800 } },	/*!< North America */
	{ GSAMP_SIZE_CR, { 425 } },					/*!< Costa Rica, Brazil */
	{ GSAMP_SIZE_UK, { 350, 400, 440 } },				/*!< UK */
};

/*!
 * \brief The default silence threshold we will use if an alternate
 * configured value is not present or is invalid.
 */
static const int DEFAULT_SILENCE_THRESHOLD = 256;

#define CONFIG_FILE_NAME "dsp.conf"

static const float dtmf_row[] = {
	697.0,  770.0,  852.0,  941.0
};
static const float dtmf_col[] = {
	1209.0, 1336.0, 1477.0, 1633.0
};
static const float mf_tones[] = {
	700.0, 900.0, 1100.0, 1300.0, 1500.0, 1700.0
};
static const char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
static const char bell_mf_positions[] = "1247C-358A--69*---0B----#";
static int thresholds[THRESHOLD_MAX];
static float dtmf_normal_twist;		/* AT&T = 8dB */
static float dtmf_reverse_twist;	/* AT&T = 4dB */
static float relax_dtmf_normal_twist;	/* AT&T = 8dB */
static float relax_dtmf_reverse_twist;	/* AT&T = 6dB */
static int dtmf_hits_to_begin;		/* How many successive hits needed to consider begin of a digit */
static int dtmf_misses_to_end;		/* How many successive misses needed to consider end of a digit */

static inline void goertzel_sample(goertzel_state_t *s, short sample)
{
	int v1;

	v1 = s->v2;
	s->v2 = s->v3;

	s->v3 = (s->fac * s->v2) >> 15;
	s->v3 = s->v3 - v1 + (sample >> s->chunky);
	if (abs(s->v3) > 32768) {
		s->chunky++;
		s->v3 = s->v3 >> 1;
		s->v2 = s->v2 >> 1;
	}
}

/*
static inline void goertzel_update(goertzel_state_t *s, short *samps, int count)
{
	int i;

	for (i = 0; i < count; i++) {
		goertzel_sample(s, samps[i]);
	}
}
*/

static inline float goertzel_result(goertzel_state_t *s)
{
	goertzel_result_t r;
	r.value = (s->v3 * s->v3) + (s->v2 * s->v2);
	r.value -= ((s->v2 * s->v3) >> 15) * s->fac;
	r.power = s->chunky * 2;
	return (float)r.value * (float)(1 << r.power);
}

static inline void goertzel_init(goertzel_state_t *s, float freq, unsigned int sample_rate)
{
	s->v2 = s->v3 = s->chunky = 0.0;
	s->fac = (int)(32768.0 * 2.0 * cos(2.0 * M_PI * freq / sample_rate));
}

static inline void goertzel_reset(goertzel_state_t *s)
{
	s->v2 = s->v3 = s->chunky = 0.0;
}

#if 0
static void mute_fragment(struct dsp *dsp, fragment_t *fragment)
{
	if (dsp->mute_fragments >= ARRAY_LEN(dsp->mute_data)) {
		syslog(LOG_ERR, "Too many fragments to mute. Ignoring\n");
		return;
	}

	dsp->mute_data[dsp->mute_fragments++] = *fragment;
}
#endif

static void tone_detect_init(tone_detect_state_t *s, int freq, int duration, int amp, unsigned int sample_rate)
{
	int duration_samples;
	float x;
	int periods_in_block;

	s->freq = freq;

	/* Desired tone duration in samples */
	duration_samples = duration * sample_rate / 1000;
	/* We want to allow 10% deviation of tone duration */
	duration_samples = duration_samples * 9 / 10;

	/* If we want to remove tone, it is important to have block size not
	   to exceed frame size. Otherwise by the moment tone is detected it is too late
	   to squelch it from previous frames. Block size is 20ms at the given sample rate.*/
	s->block_size = (20 * sample_rate) / 1000;

	periods_in_block = s->block_size * freq / sample_rate;

	/* Make sure we will have at least 5 periods at target frequency for analisys.
	   This may make block larger than expected packet and will make squelching impossible
	   but at least we will be detecting the tone */
	if (periods_in_block < 5) {
		periods_in_block = 5;
	}

	/* Now calculate final block size. It will contain integer number of periods */
	s->block_size = periods_in_block * sample_rate / freq;

	/* tone_detect is currently only used to detect fax tones and we
	   do not need squelching the fax tones */
	s->squelch = 0;

	/* Account for the first and the last block to be incomplete
	   and thus no tone will be detected in them */
	s->hits_required = (duration_samples - (s->block_size - 1)) / s->block_size;

	goertzel_init(&s->tone, freq, sample_rate);

	s->samples_pending = s->block_size;
	s->hit_count = 0;
	s->nohit_count = 0;
	s->lhit = 0;
	s->energy = 0.0;

	/* We want tone energy to be amp decibels above the rest of the signal (the noise).
	   According to Parseval's theorem the energy computed in time domain equals to energy
	   computed in frequency domain. So subtracting energy in the frequency domain (Goertzel result)
	   from the energy in the time domain we will get energy of the remaining signal (without the tone
	   we are detecting). We will be checking that
		10*syslog(Ew / (Et - Ew)) > amp
	   Calculate threshold so that we will be actually checking
		Ew > Et * threshold
	*/

	x = pow(10.0, amp / 10.0);
	s->threshold = x / (x + 1);

	if(dspdebug) syslog(1, "Setup tone %d Hz, %d ms, block_size=%d, hits_required=%d\n", freq, duration, s->block_size, s->hits_required);
}

static void fax_detect_init(struct dsp *s)
{
	tone_detect_init(&s->cng_tone_state, FAX_TONE_CNG_FREQ, FAX_TONE_CNG_DURATION, FAX_TONE_CNG_DB, s->sample_rate);
	tone_detect_init(&s->ced_tone_state, FAX_TONE_CED_FREQ, FAX_TONE_CED_DURATION, FAX_TONE_CED_DB, s->sample_rate);
	if (s->faxmode & DSP_FAXMODE_DETECT_SQUELCH) {
		s->cng_tone_state.squelch = 1;
		s->ced_tone_state.squelch = 1;
	}

}

static void dtmf_detect_init(dtmf_detect_state_t *s, unsigned int sample_rate)
{
	int i;

	for (i = 0; i < 4; i++) {
		goertzel_init(&s->row_out[i], dtmf_row[i], sample_rate);
		goertzel_init(&s->col_out[i], dtmf_col[i], sample_rate);
	}
	s->lasthit = 0;
	s->current_hit = 0;
	s->energy = 0.0;
	s->current_sample = 0;
	s->hits = 0;
	s->misses = 0;
}

static void mf_detect_init(mf_detect_state_t *s, unsigned int sample_rate)
{
	int i;

	for (i = 0; i < 6; i++) {
		goertzel_init(&s->tone_out[i], mf_tones[i], sample_rate);
	}
	s->hits[0] = s->hits[1] = s->hits[2] = s->hits[3] = s->hits[4] = 0;
	s->current_sample = 0;
	s->current_hit = 0;
}

static void digit_detect_init(digit_detect_state_t *s, int mf, unsigned int sample_rate)
{
	s->current_digits = 0;
	s->detected_digits = 0;
	s->lost_digits = 0;
	s->digits[0] = '\0';

	if (mf) {
		mf_detect_init(&s->td.mf, sample_rate);
	} else {
		dtmf_detect_init(&s->td.dtmf, sample_rate);
	}
}

static int tone_detect(struct dsp */*dsp*/, tone_detect_state_t *s, int16_t *amp, int samples, int nohit_limit)
{
	float tone_energy;
	int i;
	int hit = 0;
	int limit;
	int res = 0;
	int16_t *ptr;
	short samp;
	int start, end;
	fragment_t mute = {0, 0};

	if (s->squelch && s->mute_samples > 0) {
		mute.end = (s->mute_samples < samples) ? s->mute_samples : samples;
		s->mute_samples -= mute.end;
	}

	for (start = 0; start < samples; start = end) {
		/* Process in blocks. */
		limit = samples - start;
		if (limit > s->samples_pending) {
			limit = s->samples_pending;
		}
		end = start + limit;

		for (i = limit, ptr = amp ; i > 0; i--, ptr++) {
			samp = *ptr;
			/* signed 32 bit int should be enough to square any possible signed 16 bit value */
			s->energy += (int32_t) samp * (int32_t) samp;

			goertzel_sample(&s->tone, samp);
		}

		s->samples_pending -= limit;

		if (s->samples_pending) {
			/* Finished incomplete (last) block */
			break;
		}

		tone_energy = goertzel_result(&s->tone);

		/* Scale to make comparable */
		tone_energy *= 2.0;
		s->energy *= s->block_size;

		//if(dspdebug) syslog(10, "tone %d, Ew=%.2E, Et=%.2E, s/n=%10.2f\n", s->freq, tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
		hit = 0;
		if (tone_energy > s->energy * s->threshold) {
			if(dspdebug) syslog(10, "Hit! count=%d\n", s->hit_count);
			hit = 1;
		}

		if (s->hit_count) {
			s->hit_count++;
		}

		if (hit == s->lhit) {
			if (!hit) {
				++s->nohit_count;
				if (s->nohit_count >= nohit_limit) {
					/* (nohit_limit + 1) successive misses. Tone ended */
					s->hit_count = 0;
				}
			} else if (!s->hit_count) {
				s->hit_count++;
				s->nohit_count = 0;
			}

		}

		if (s->hit_count == s->hits_required) {
			if(dspdebug) syslog(1, "%d Hz done detected\n", s->freq);
			res = 1;
		}

		s->lhit = hit;

#if 0 
		/* If we had a hit in this block, include it into mute fragment */
		if (s->squelch && hit) {
			if (mute.end < start - s->block_size) {
				/* There is a gap between fragments */
				mute_fragment(dsp, &mute);
				mute.start = (start > s->block_size) ? (start - s->block_size) : 0;
			}
			mute.end = end + s->block_size;
		}
#endif

		/* Reinitialise the detector for the next block */
		/* Reset for the next block */
		goertzel_reset(&s->tone);

		/* Advance to the next block */
		s->energy = 0.0;
		s->samples_pending = s->block_size;

		amp += limit;
	}

#if 0 
	if (s->squelch && mute.end) {
		if (mute.end > samples) {
			s->mute_samples = mute.end - samples;
			mute.end = samples;
		}
		mute_fragment(dsp, &mute);
	}
#endif

	return res;
}

static void store_digit(digit_detect_state_t *s, char digit)
{
	s->detected_digits++;
	if (s->current_digits < MAX_DTMF_DIGITS) {
		s->digitlen[s->current_digits] = 0;
		s->digits[s->current_digits++] = digit;
		s->digits[s->current_digits] = '\0';
	} else {
		syslog(4, "Digit lost due to full buffer");
		s->lost_digits++;
	}
}

static int dtmf_detect(struct dsp */*dsp*/, digit_detect_state_t *s, int16_t amp[], int samples, int /*squelch*/, int relax)
{
	float row_energy[4];
	float col_energy[4];
	int i;
	int j;
	int sample;
	short samp;
	int best_row;
	int best_col;
	int hit;
	int limit;
#if 0
	fragment_t mute = {0, 0};

	if (squelch && s->td.dtmf.mute_samples > 0) {
		mute.end = (s->td.dtmf.mute_samples < samples) ? s->td.dtmf.mute_samples : samples;
		s->td.dtmf.mute_samples -= mute.end;
	}
#endif

	hit = 0;
	for (sample = 0; sample < samples; sample = limit) {
		/* DTMF_GSIZE is optimised to meet the DTMF specs. */
		if ((samples - sample) >= (DTMF_GSIZE - s->td.dtmf.current_sample)) {
			limit = sample + (DTMF_GSIZE - s->td.dtmf.current_sample);
		} else {
			limit = samples;
		}
		/* The following unrolled loop takes only 35% (rough estimate) of the
		   time of a rolled loop on the machine on which it was developed */
		for (j = sample; j < limit; j++) {
			samp = amp[j];
			s->td.dtmf.energy += (int32_t) samp * (int32_t) samp;
			/* With GCC 2.95, the following unrolled code seems to take about 35%
			   (rough estimate) as long as a neat little 0-3 loop */
			goertzel_sample(s->td.dtmf.row_out, samp);
			goertzel_sample(s->td.dtmf.col_out, samp);
			goertzel_sample(s->td.dtmf.row_out + 1, samp);
			goertzel_sample(s->td.dtmf.col_out + 1, samp);
			goertzel_sample(s->td.dtmf.row_out + 2, samp);
			goertzel_sample(s->td.dtmf.col_out + 2, samp);
			goertzel_sample(s->td.dtmf.row_out + 3, samp);
			goertzel_sample(s->td.dtmf.col_out + 3, samp);
		}
		s->td.dtmf.current_sample += (limit - sample);
		if (s->td.dtmf.current_sample < DTMF_GSIZE) {
			continue;
		}
		/* We are at the end of a DTMF detection block */
		/* Find the peak row and the peak column */
		row_energy[0] = goertzel_result(&s->td.dtmf.row_out[0]);
		col_energy[0] = goertzel_result(&s->td.dtmf.col_out[0]);

		for (best_row = best_col = 0, i = 1; i < 4; i++) {
			row_energy[i] = goertzel_result(&s->td.dtmf.row_out[i]);
			if (row_energy[i] > row_energy[best_row]) {
				best_row = i;
			}
			col_energy[i] = goertzel_result(&s->td.dtmf.col_out[i]);
			if (col_energy[i] > col_energy[best_col]) {
				best_col = i;
			}
		}
		hit = 0;
		/* Basic signal level test and the twist test */
		if (row_energy[best_row] >= DTMF_THRESHOLD &&
		    col_energy[best_col] >= DTMF_THRESHOLD &&
		    col_energy[best_col] < row_energy[best_row] * (relax ? relax_dtmf_reverse_twist : dtmf_reverse_twist) &&
		    row_energy[best_row] < col_energy[best_col] * (relax ? relax_dtmf_normal_twist : dtmf_normal_twist)) {
			/* Relative peak test */
			for (i = 0; i < 4; i++) {
				if ((i != best_col &&
				    col_energy[i] * DTMF_RELATIVE_PEAK_COL > col_energy[best_col]) ||
				    (i != best_row
				     && row_energy[i] * DTMF_RELATIVE_PEAK_ROW > row_energy[best_row])) {
					break;
				}
			}
			/* ... and fraction of total energy test */
			if (i >= 4 &&
			    (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY * s->td.dtmf.energy) {
				/* Got a hit */
				hit = dtmf_positions[(best_row << 2) + best_col];
			}
		}

/*
 * Adapted from ETSI ES 201 235-3 V1.3.1 (2006-03)
 * (40ms reference is tunable with hits_to_begin and misses_to_end)
 * each hit/miss is 12.75ms with DTMF_GSIZE at 102
 *
 * Character recognition: When not DRC *(1) and then
 *      Shall exist VSC > 40 ms (hits_to_begin)
 *      May exist 20 ms <= VSC <= 40 ms
 *      Shall not exist VSC < 20 ms
 *
 * Character recognition: When DRC and then
 *      Shall cease Not VSC > 40 ms (misses_to_end)
 *      May cease 20 ms >= Not VSC >= 40 ms
 *      Shall not cease Not VSC < 20 ms
 *
 * *(1) or optionally a different digit recognition condition
 *
 * Legend: VSC The continuous existence of a valid signal condition.
 *      Not VSC The continuous non-existence of valid signal condition.
 *      DRC The existence of digit recognition condition.
 *      Not DRC The non-existence of digit recognition condition.
 */

/*
 * Example: hits_to_begin=2 misses_to_end=3
 * -------A lhit=A hits=0&1
 * ------AA hits=2 current_hit=A misses=0       BEGIN A
 * -----AA- misses=1 lhit=' ' hits=0
 * ----AA-- misses=2
 * ---AA--- misses=3 current_hit=' '            END A
 * --AA---B lhit=B hits=0&1
 * -AA---BC lhit=C hits=0&1
 * AA---BCC hits=2 current_hit=C misses=0       BEGIN C
 * A---BCC- misses=1 lhit=' ' hits=0
 * ---BCC-C misses=0 lhit=C hits=0&1
 * --BCC-CC misses=0
 *
 * Example: hits_to_begin=3 misses_to_end=2
 * -------A lhit=A hits=0&1
 * ------AA hits=2
 * -----AAA hits=3 current_hit=A misses=0       BEGIN A
 * ----AAAB misses=1 lhit=B hits=0&1
 * ---AAABB misses=2 current_hit=' ' hits=2     END A
 * --AAABBB hits=3 current_hit=B misses=0       BEGIN B
 * -AAABBBB misses=0
 *
 * Example: hits_to_begin=2 misses_to_end=2
 * -------A lhit=A hits=0&1
 * ------AA hits=2 current_hit=A misses=0       BEGIN A
 * -----AAB misses=1 hits=0&1
 * ----AABB misses=2 current_hit=' ' hits=2 current_hit=B misses=0 BEGIN B
 * ---AABBB misses=0
 */

		if (s->td.dtmf.current_hit) {
			/* We are in the middle of a digit already */
			if (hit != s->td.dtmf.current_hit) {
				s->td.dtmf.misses++;
				if (s->td.dtmf.misses == dtmf_misses_to_end) {
					/* There were enough misses to consider digit ended */
					s->td.dtmf.current_hit = 0;
				}
			} else {
				s->td.dtmf.misses = 0;
				/* Current hit was same as last, so increment digit duration (of last digit) */
				s->digitlen[s->current_digits - 1] += DTMF_GSIZE;
			}
		}

		/* Look for a start of a new digit no matter if we are already in the middle of some
		   digit or not. This is because hits_to_begin may be smaller than misses_to_end
		   and we may find begin of new digit before we consider last one ended. */

		if (hit != s->td.dtmf.lasthit) {
			s->td.dtmf.lasthit = hit;
			s->td.dtmf.hits = 0;
		}
		if (hit && hit != s->td.dtmf.current_hit) {
			s->td.dtmf.hits++;
			if (s->td.dtmf.hits == dtmf_hits_to_begin) {
				store_digit(s, hit);
				s->digitlen[s->current_digits - 1] = dtmf_hits_to_begin * DTMF_GSIZE;
				s->td.dtmf.current_hit = hit;
				s->td.dtmf.misses = 0;
			}
		}

		/* If we had a hit in this block, include it into mute fragment */
#if 0
		if (squelch && hit) {
			if (mute.end < sample - DTMF_GSIZE) {
				/* There is a gap between fragments */
				mute_fragment(dsp, &mute);
				mute.start = (sample > DTMF_GSIZE) ? (sample - DTMF_GSIZE) : 0;
			}
			mute.end = limit + DTMF_GSIZE;
		}
#endif

		/* Reinitialise the detector for the next block */
		for (i = 0; i < 4; i++) {
			goertzel_reset(&s->td.dtmf.row_out[i]);
			goertzel_reset(&s->td.dtmf.col_out[i]);
		}
		s->td.dtmf.energy = 0.0;
		s->td.dtmf.current_sample = 0;
	}

#if 0
	if (squelch && mute.end) {
		if (mute.end > samples) {
			s->td.dtmf.mute_samples = mute.end - samples;
			mute.end = samples;
		}
		mute_fragment(dsp, &mute);
	}
#endif

	return (s->td.dtmf.current_hit);	/* return the debounced hit */
}

static int mf_detect(struct dsp */*dsp*/, digit_detect_state_t *s, int16_t amp[],
		int samples, int /*squelch*/, int /*relax*/)
{
	float energy[6];
	int best;
	int second_best;
	int i;
	int j;
	int sample;
	short samp;
	int hit;
	int limit;

	hit = 0;
	for (sample = 0; sample < samples; sample = limit) {
		/* 80 is optimised to meet the MF specs. */
		/* XXX So then why is MF_GSIZE defined as 120? */
		if ((samples - sample) >= (MF_GSIZE - s->td.mf.current_sample)) {
			limit = sample + (MF_GSIZE - s->td.mf.current_sample);
		} else {
			limit = samples;
		}
		/* The following unrolled loop takes only 35% (rough estimate) of the
		   time of a rolled loop on the machine on which it was developed */
		for (j = sample; j < limit; j++) {
			/* With GCC 2.95, the following unrolled code seems to take about 35%
			   (rough estimate) as long as a neat little 0-3 loop */
			samp = amp[j];
			goertzel_sample(s->td.mf.tone_out, samp);
			goertzel_sample(s->td.mf.tone_out + 1, samp);
			goertzel_sample(s->td.mf.tone_out + 2, samp);
			goertzel_sample(s->td.mf.tone_out + 3, samp);
			goertzel_sample(s->td.mf.tone_out + 4, samp);
			goertzel_sample(s->td.mf.tone_out + 5, samp);
		}
		s->td.mf.current_sample += (limit - sample);
		if (s->td.mf.current_sample < MF_GSIZE) {
			continue;
		}
		/* We're at the end of an MF detection block.  */
		/* Find the two highest energies. The spec says to look for
		   two tones and two tones only. Taking this literally -ie
		   only two tones pass the minimum threshold - doesn't work
		   well. The sinc function mess, due to rectangular windowing
		   ensure that! Find the two highest energies and ensure they
		   are considerably stronger than any of the others. */
		energy[0] = goertzel_result(&s->td.mf.tone_out[0]);
		energy[1] = goertzel_result(&s->td.mf.tone_out[1]);
		if (energy[0] > energy[1]) {
			best = 0;
			second_best = 1;
		} else {
			best = 1;
			second_best = 0;
		}
		/*endif*/
		for (i = 2; i < 6; i++) {
			energy[i] = goertzel_result(&s->td.mf.tone_out[i]);
			if (energy[i] >= energy[best]) {
				second_best = best;
				best = i;
			} else if (energy[i] >= energy[second_best]) {
				second_best = i;
			}
		}
		/* Basic signal level and twist tests */
		hit = 0;
		if (energy[best] >= BELL_MF_THRESHOLD && energy[second_best] >= BELL_MF_THRESHOLD
		    && energy[best] < energy[second_best]*BELL_MF_TWIST
		    && energy[best] * BELL_MF_TWIST > energy[second_best]) {
			/* Relative peak test */
			hit = -1;
			for (i = 0; i < 6; i++) {
				if (i != best && i != second_best) {
					if (energy[i]*BELL_MF_RELATIVE_PEAK >= energy[second_best]) {
						/* The best two are not clearly the best */
						hit = 0;
						break;
					}
				}
			}
		}
		if (hit) {
			/* Get the values into ascending order */
			if (second_best < best) {
				i = best;
				best = second_best;
				second_best = i;
			}
			best = best * 5 + second_best - 1;
			hit = bell_mf_positions[best];
			/* Look for two successive similar results */
			/* The logic in the next test is:
			   For KP we need 4 successive identical clean detects, with
			   two blocks of something different preceeding it. For anything
			   else we need two successive identical clean detects, with
			   two blocks of something different preceeding it. */
			if (hit == s->td.mf.hits[4] && hit == s->td.mf.hits[3] &&
			   ((hit != '*' && hit != s->td.mf.hits[2] && hit != s->td.mf.hits[1])||
			    (hit == '*' && hit == s->td.mf.hits[2] && hit != s->td.mf.hits[1] &&
			    hit != s->td.mf.hits[0]))) {
				store_digit(s, hit);
			}
		}


		if (hit != s->td.mf.hits[4] && hit != s->td.mf.hits[3]) {
			/* Two successive block without a hit terminate current digit */
			s->td.mf.current_hit = 0;
		}

		s->td.mf.hits[0] = s->td.mf.hits[1];
		s->td.mf.hits[1] = s->td.mf.hits[2];
		s->td.mf.hits[2] = s->td.mf.hits[3];
		s->td.mf.hits[3] = s->td.mf.hits[4];
		s->td.mf.hits[4] = hit;

		/* If we had a hit in this block, include it into mute fragment */
#if 0
		if (squelch && hit) {
			if (mute.end < sample - MF_GSIZE) {
				/* There is a gap between fragments */
				mute_fragment(dsp, &mute);
				mute.start = (sample > MF_GSIZE) ? (sample - MF_GSIZE) : 0;
			}
			mute.end = limit + MF_GSIZE;
		}
#endif

		/* Reinitialise the detector for the next block */
		for (i = 0; i < 6; i++) {
			goertzel_reset(&s->td.mf.tone_out[i]);
		}
		s->td.mf.current_sample = 0;
	}

#if 0
	if (squelch && mute.end) {
		if (mute.end > samples) {
			s->td.mf.mute_samples = mute.end - samples;
			mute.end = samples;
		}
		mute_fragment(dsp, &mute);
	}
#endif

	return (s->td.mf.current_hit); /* return the debounced hit */
}

static inline int pair_there(float p1, float p2, float i1, float i2, float e)
{
	/* See if p1 and p2 are there, relative to i1 and i2 and total energy */
	/* Make sure absolute levels are high enough */
	if ((p1 < TONE_MIN_THRESH) || (p2 < TONE_MIN_THRESH)) {
		return 0;
	}
	/* Amplify ignored stuff */
	i2 *= TONE_THRESH;
	i1 *= TONE_THRESH;
	e *= TONE_THRESH;
	/* Check first tone */
	if ((p1 < i1) || (p1 < i2) || (p1 < e)) {
		return 0;
	}
	/* And second */
	if ((p2 < i1) || (p2 < i2) || (p2 < e)) {
		return 0;
	}
	/* Guess it's there... */
	return 1;
}

static int __dsp_call_progress(struct dsp *dsp, short *s, int len)
{
	short samp;
	int x;
	int y;
	int pass;
	int newstate = DSP_TONE_STATE_SILENCE;
	int res = 0;
	while (len) {
		/* Take the lesser of the number of samples we need and what we have */
		pass = len;
		if (pass > dsp->gsamp_size - dsp->gsamps) {
			pass = dsp->gsamp_size - dsp->gsamps;
		}
		for (x = 0; x < pass; x++) {
			samp = s[x];
			dsp->genergy += (int32_t) samp * (int32_t) samp;
			for (y = 0; y < dsp->freqcount; y++) {
				goertzel_sample(&dsp->freqs[y], samp);
			}
		}
		s += pass;
		dsp->gsamps += pass;
		len -= pass;
		if (dsp->gsamps == dsp->gsamp_size) {
			float hz[10];
			for (y = 0; y < 10; y++) {
				hz[y] = goertzel_result(&dsp->freqs[y]);
			}
			switch (dsp->progmode) {
			case PROG_MODE_NA:
				if (pair_there(hz[HZ_480], hz[HZ_620], hz[HZ_350], hz[HZ_440], dsp->genergy)) {
					newstate = DSP_TONE_STATE_BUSY;
				} else if (pair_there(hz[HZ_440], hz[HZ_480], hz[HZ_350], hz[HZ_620], dsp->genergy)) {
					newstate = DSP_TONE_STATE_RINGING;
				} else if (pair_there(hz[HZ_350], hz[HZ_440], hz[HZ_480], hz[HZ_620], dsp->genergy)) {
					newstate = DSP_TONE_STATE_DIALTONE;
				} else if (pair_there(hz[HZ_400], hz[HZ_450], hz[HZ_400], hz[HZ_400], dsp->genergy)) {
					//UK RINGING
					newstate = DSP_TONE_STATE_RINGING;
				} else if (hz[HZ_425] > TONE_MIN_THRESH * TONE_THRESH) {
					//CZECH / europe RINGING
					newstate = DSP_TONE_STATE_RINGING;
				} else if (hz[HZ_950] > TONE_MIN_THRESH * TONE_THRESH) {
					newstate = DSP_TONE_STATE_SPECIAL1;
				} else if (hz[HZ_1400] > TONE_MIN_THRESH * TONE_THRESH) {
					/* End of SPECIAL1 or middle of SPECIAL2 */
					if (dsp->tstate == DSP_TONE_STATE_SPECIAL1 || dsp->tstate == DSP_TONE_STATE_SPECIAL2) {
						newstate = DSP_TONE_STATE_SPECIAL2;
					}
				} else if (hz[HZ_1800] > TONE_MIN_THRESH * TONE_THRESH) {
					/* End of SPECIAL2 or middle of SPECIAL3 */
					if (dsp->tstate == DSP_TONE_STATE_SPECIAL2 || dsp->tstate == DSP_TONE_STATE_SPECIAL3) {
						newstate = DSP_TONE_STATE_SPECIAL3;
					}
				} else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
					newstate = DSP_TONE_STATE_TALKING;
				} else {
					newstate = DSP_TONE_STATE_SILENCE;
				}
				break;
#if 0
			case PROG_MODE_CR:
				if (hz[HZ_425] > TONE_MIN_THRESH * TONE_THRESH) {
					newstate = DSP_TONE_STATE_RINGING;
				} else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
					newstate = DSP_TONE_STATE_TALKING;
				} else {
					newstate = DSP_TONE_STATE_SILENCE;
				}
				break;
			case PROG_MODE_UK:
				if (hz[HZ_400UK] > TONE_MIN_THRESH * TONE_THRESH) {
					newstate = DSP_TONE_STATE_HUNGUP;
				} else if (pair_there(hz[HZ_350UK], hz[HZ_440UK], hz[HZ_400UK], hz[HZ_400UK], dsp->genergy)) {
					newstate = DSP_TONE_STATE_DIALTONE;
				}
				break;
#endif
			default:
				syslog(LOG_WARNING, "Can't process in unknown prog mode '%u'\n", dsp->progmode);
			}
			if (newstate == dsp->tstate) {
				dsp->tcount++;
				if (dsp->ringtimeout) {
					dsp->ringtimeout++;
				}
				switch (dsp->tstate) {
				case DSP_TONE_STATE_RINGING:
					if ((dsp->features & DSP_PROGRESS_RINGING) &&
					    (dsp->tcount == THRESH_RING)) {
						res = AST_CONTROL_RINGING;
						dsp->ringtimeout = 1;
					}
					break;
				case DSP_TONE_STATE_BUSY:
					if ((dsp->features & DSP_PROGRESS_BUSY) &&
					    (dsp->tcount == THRESH_BUSY)) {
						res = AST_CONTROL_BUSY;
						dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
					}
					break;
				case DSP_TONE_STATE_TALKING:
					if ((dsp->features & DSP_PROGRESS_TALK) &&
					    (dsp->tcount == THRESH_TALK)) {
						res = AST_CONTROL_ANSWER;
						dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
					}
					break;
				case DSP_TONE_STATE_SPECIAL3:
					if ((dsp->features & DSP_PROGRESS_CONGESTION) &&
					    (dsp->tcount == THRESH_CONGESTION)) {
						res = AST_CONTROL_CONGESTION;
						dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
					}
					break;
				case DSP_TONE_STATE_HUNGUP:
					if ((dsp->features & DSP_FEATURE_CALL_PROGRESS) &&
					    (dsp->tcount == THRESH_HANGUP)) {
						res = AST_CONTROL_HANGUP;
						dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
					}
					break;
				}
				if (dsp->ringtimeout == THRESH_RING2ANSWER) {
					if(dspdebug) syslog(1, "Consider call as answered because of timeout after last ring\n");
					res = AST_CONTROL_ANSWER;
					dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
				}
			} else {
				if(dspdebug) syslog(5, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount);
				if(dspdebug) syslog(5, "Start state %d\n", newstate);
				dsp->tstate = newstate;
				dsp->tcount = 1;
			}

			/* Reset goertzel */
			for (x = 0; x < 10; x++) {
				dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
			}
			dsp->gsamps = 0;
			dsp->genergy = 0.0;
		}
	}

	return res;
}

int dsp_call_progress(struct dsp *dsp, short *data, int samples)
{
	return __dsp_call_progress(dsp, data, samples);
}

static int __dsp_silence_noise(struct dsp *dsp, short *s, int len, int *totalsilence, int *totalnoise, u_int16_t *frames_energy)
{
	int accum;
	int x;
	int res = 0;

	if (!len) {
		return 0;
	}

	dsp->received += len/160;

	accum = 0;
	for (x = 0; x < len; x++) {
		if(s[x] <= 1) {
			if(dsp->last_zero == true) {
				dsp->counter++;
			}
			dsp->last_zero = true;
		} else {
			dsp->counter = 0;
			dsp->last_zero = false;
		}
		accum += abs(s[x]);
	}
	if(dsp->counter >= 159 and dsp->counter <= 195) { // tolerance 159-195 frames for 20ms 
		// 20ms (8khz) silence - count loss and reset counter;
		dsp->loss++;  // number of consecutive silence 20ms frames 
		//if(dsp->counter > 161) printf("hit los %u ++ %d\n", dsp->counter, dsp->loss);
		dsp->counter = 0;
	} else {
		if(dsp->loss) {
			if(dsp->loss <= 3) {
				// store loss only if there were max 3 consecutive lost silence frames
				dsp->loss_hist[dsp->loss]++;
				dsp->loss = 0;
			} else {
				// any consecutive loss > 2 resets loss counter to 0
				dsp->loss = 0;
			}
		}
		dsp->loss = 0;
	}
	accum /= len;
	if (accum < dsp->threshold) {
		/* Silent */
		dsp->totalsilence += len / (dsp->sample_rate / 1000);
		if (dsp->totalnoise) {
			/* Move and save history */
			memmove(dsp->historicnoise + DSP_HISTORY - dsp->busycount, dsp->historicnoise + DSP_HISTORY - dsp->busycount + 1, dsp->busycount * sizeof(dsp->historicnoise[0]));
			dsp->historicnoise[DSP_HISTORY - 1] = dsp->totalnoise;
/* we don't want to check for busydetect that frequently */
#if 0
			dsp->busymaybe = 1;
#endif
		}
		dsp->totalnoise = 0;
		res = 1;
	} else {
		/* Not silent */
		dsp->totalnoise += len / (dsp->sample_rate / 1000);
		if (dsp->totalsilence) {
			int silence1 = dsp->historicsilence[DSP_HISTORY - 1];
			int silence2 = dsp->historicsilence[DSP_HISTORY - 2];
			/* Move and save history */
			memmove(dsp->historicsilence + DSP_HISTORY - dsp->busycount, dsp->historicsilence + DSP_HISTORY - dsp->busycount + 1, dsp->busycount * sizeof(dsp->historicsilence[0]));
			dsp->historicsilence[DSP_HISTORY - 1] = dsp->totalsilence;
			/* check if the previous sample differs only by BUSY_PERCENT from the one before it */
			if (silence1 < silence2) {
				if (silence1 + silence1 * BUSY_PERCENT / 100 >= silence2) {
					dsp->busymaybe = 1;
				} else {
					dsp->busymaybe = 0;
				}
			} else {
				if (silence1 - silence1 * BUSY_PERCENT / 100 <= silence2) {
					dsp->busymaybe = 1;
				} else {
					dsp->busymaybe = 0;
				}
			}
		}
		dsp->totalsilence = 0;
	}
	if (totalsilence) {
		*totalsilence = dsp->totalsilence;
	}
	if (totalnoise) {
		*totalnoise = dsp->totalnoise;
	}
	if (frames_energy) {
		*frames_energy = accum;
	}
	return res;
}

int dsp_busydetect(struct dsp *dsp)
{
	int res = 0, x;
#ifndef BUSYDETECT_TONEONLY
	int avgsilence = 0, hitsilence = 0;
#endif
	int avgtone = 0, hittone = 0;

	/* if we have a 4 length pattern, the way busymaybe is set doesn't help us. */
	if (dsp->busy_cadence.length != 4) {
		if (!dsp->busymaybe) {
			return res;
		}
	}

	for (x = DSP_HISTORY - dsp->busycount; x < DSP_HISTORY; x++) {
#ifndef BUSYDETECT_TONEONLY
		avgsilence += dsp->historicsilence[x];
#endif
		avgtone += dsp->historicnoise[x];
	}
#ifndef BUSYDETECT_TONEONLY
	avgsilence /= dsp->busycount;
#endif
	avgtone /= dsp->busycount;
	for (x = DSP_HISTORY - dsp->busycount; x < DSP_HISTORY; x++) {
#ifndef BUSYDETECT_TONEONLY
		if (avgsilence > dsp->historicsilence[x]) {
			if (avgsilence - (avgsilence * BUSY_PERCENT / 100) <= dsp->historicsilence[x]) {
				hitsilence++;
			}
		} else {
			if (avgsilence + (avgsilence * BUSY_PERCENT / 100) >= dsp->historicsilence[x]) {
				hitsilence++;
			}
		}
#endif
		if (avgtone > dsp->historicnoise[x]) {
			if (avgtone - (avgtone * BUSY_PERCENT / 100) <= dsp->historicnoise[x]) {
				hittone++;
			}
		} else {
			if (avgtone + (avgtone * BUSY_PERCENT / 100) >= dsp->historicnoise[x]) {
				hittone++;
			}
		}
	}
#ifndef BUSYDETECT_TONEONLY
	if ((hittone >= dsp->busycount - 1) && (hitsilence >= dsp->busycount - 1) &&
	    (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX) &&
	    (avgsilence >= BUSY_MIN && avgsilence <= BUSY_MAX)) {
#else
	if ((hittone >= dsp->busycount - 1) && (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX)) {
#endif
#ifdef BUSYDETECT_COMPARE_TONE_AND_SILENCE
		if (avgtone > avgsilence) {
			if (avgtone - avgtone*BUSY_PERCENT/100 <= avgsilence) {
				res = 1;
			}
		} else {
			if (avgtone + avgtone*BUSY_PERCENT/100 >= avgsilence) {
				res = 1;
			}
		}
#else
		res = 1;
#endif
	}

	/* If we have a 4-length pattern, we can go ahead and just check it in a different way. */
	if (dsp->busy_cadence.length == 4) {
		int x;
		int errors = 0;
		int errors_max = ((4 * dsp->busycount) / 100.0) * BUSY_PAT_PERCENT;

		for (x = DSP_HISTORY - (dsp->busycount); x < DSP_HISTORY; x += 2) {
			int temp_error;
			temp_error = abs(dsp->historicnoise[x] - dsp->busy_cadence.pattern[0]);
			if ((temp_error * 100) / dsp->busy_cadence.pattern[0] > BUSY_PERCENT) {
				errors++;
			}

			temp_error = abs(dsp->historicnoise[x + 1] - dsp->busy_cadence.pattern[2]);
			if ((temp_error * 100) / dsp->busy_cadence.pattern[2] > BUSY_PERCENT) {
				errors++;
			}

			temp_error = abs(dsp->historicsilence[x] - dsp->busy_cadence.pattern[1]);
			if ((temp_error * 100) / dsp->busy_cadence.pattern[1] > BUSY_PERCENT) {
				errors++;
			}

			temp_error = abs(dsp->historicsilence[x + 1] - dsp->busy_cadence.pattern[3]);
			if ((temp_error * 100) / dsp->busy_cadence.pattern[3] > BUSY_PERCENT) {
				errors++;
			}
		}

		if(dspdebug) syslog(5, "errors = %d  max = %d\n", errors, errors_max);

		if (errors <= errors_max) {
			return 1;
		}
	}

	/* If we know the expected busy tone length, check we are in the range */
	if (res && (dsp->busy_cadence.pattern[0] > 0)) {
		if (abs(avgtone - dsp->busy_cadence.pattern[0]) > MAX(dsp->busy_cadence.pattern[0]*BUSY_PAT_PERCENT/100, 20)) {
#ifdef BUSYDETECT_DEBUG
			if(dspdebug) syslog(5, "busy detector: avgtone of %d not close enough to desired %d\n",
				avgtone, dsp->busy_cadence.pattern[0]);
#endif
			res = 0;
		}
	}
#ifndef BUSYDETECT_TONEONLY
	/* If we know the expected busy tone silent-period length, check we are in the range */
	if (res && (dsp->busy_cadence.pattern[1] > 0)) {
		if (abs(avgsilence - dsp->busy_cadence.pattern[1]) > MAX(dsp->busy_cadence.pattern[1]*BUSY_PAT_PERCENT/100, 20)) {
#ifdef BUSYDETECT_DEBUG
		if(dspdebug) syslog(5, "busy detector: avgsilence of %d not close enough to desired %d\n",
			avgsilence, dsp->busy_cadence.pattern[1]);
#endif
			res = 0;
		}
	}
#endif
#if !defined(BUSYDETECT_TONEONLY) && defined(BUSYDETECT_DEBUG)
	if (res) {
		if(dspdebug) syslog(5, "dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
	} else {
		if(dspdebug) syslog(5, "busy detector: FAILED with avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
	}
#endif
	return res;
}

static int dsp_silence_noise_with_energy(struct dsp *dsp, short *data, int len, int *total, u_int16_t *frames_energy, int noise)
{
	if (noise) {
		return __dsp_silence_noise(dsp, data, len, NULL, total, frames_energy);
	} else {
		return __dsp_silence_noise(dsp, data, len, total, NULL, frames_energy);
	}
}

int dsp_silence_with_energy(struct dsp *dsp, short *data, int len, int *totalsilence, u_int16_t *frames_energy)
{
	return dsp_silence_noise_with_energy(dsp, data, len, totalsilence, frames_energy, 0);
}

int dsp_silence(struct dsp *dsp, short *data, int len, int *totalsilence)
{
	return dsp_silence_noise_with_energy(dsp, data, len, totalsilence, NULL, 0);
}

int dsp_noise(struct dsp *dsp, short *data, int len, int *totalnoise)
{
	return dsp_silence_noise_with_energy(dsp, data, len, totalnoise, NULL, 1);
}


int dsp_process(struct dsp *dsp, short *shortdata, int len, char *event_digit, int *event_len, int *silence, int *totalsilence, int *totalnoise, int *res_call_progress, u_int16_t *energylevel)
{
	int res = 0;
	int digit = 0, fax_digit = 0;

	if(event_len) {
		*event_len = 0;
	}
	if(event_digit) {
		*event_digit = 0;
	}
	if(res_call_progress) {
		*res_call_progress = 0;
	}
	
	/* Initially we do not want to mute anything */
	dsp->mute_fragments = 0;

	/* Need to run the silence detection stuff for silence suppression and busy detection */
	if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) || (dsp->features & DSP_FEATURE_BUSY_DETECT) || (dsp->features & DSP_FEATURE_ENERGYLEVEL)) {
		*silence = __dsp_silence_noise(dsp, shortdata, len, totalsilence, totalnoise, energylevel);
		//if(dspdebug) syslog(1, "silence [%u] noise [%u]\n", *totalsilence, *totalnoise);
	}

	if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) {
		res |= DSP_PROCESS_RES_SILENCE; //silence
	}

	if ((dsp->features & DSP_FEATURE_BUSY_DETECT) && dsp_busydetect(dsp)) {
		if(dspdebug) syslog(1, "busy tone was detected");
		res |= DSP_PROCESS_RES_BUSY; // busy detected
	}

	if ((dsp->features & DSP_FEATURE_FAX_DETECT)) {
		if ((dsp->faxmode & DSP_FAXMODE_DETECT_CNG) && tone_detect(dsp, &dsp->cng_tone_state, shortdata, len, 1)) {
			fax_digit = 'f';
		}

		if ((dsp->faxmode & DSP_FAXMODE_DETECT_CED) && tone_detect(dsp, &dsp->ced_tone_state, shortdata, len, 2)) {
			fax_digit = 'e';
		}
	}

	if (dsp->features & (DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_BUSY_DETECT)) {
		if (dsp->digitmode & DSP_DIGITMODE_MF) {
			digit = mf_detect(dsp, &dsp->digit_state, shortdata, len, (dsp->digitmode & DSP_DIGITMODE_NOQUELCH) == 0, (dsp->digitmode & DSP_DIGITMODE_RELAXDTMF));
		} else {
			digit = dtmf_detect(dsp, &dsp->digit_state, shortdata, len, (dsp->digitmode & DSP_DIGITMODE_NOQUELCH) == 0, (dsp->digitmode & DSP_DIGITMODE_RELAXDTMF));
		}

		if (dsp->digit_state.current_digits) {
			int event = 0;
			*event_len = 0;
			*event_digit = 0;

			if (!dsp->dtmf_began) {
				/* We have not reported DTMF_BEGIN for anything yet */

				if (dsp->features & DSP_FEATURE_DIGIT_DETECT) {
					event = AST_FRAME_DTMF_BEGIN;
					*event_digit = dsp->digit_state.digits[0];
				}
				dsp->dtmf_began = 1;

			} else if (dsp->digit_state.current_digits > 1 || digit != dsp->digit_state.digits[0]) {
				/* Digit changed. This means digit we have reported with DTMF_BEGIN ended */
				if (dsp->features & DSP_FEATURE_DIGIT_DETECT) {
					event = AST_FRAME_DTMF_END;
					*event_digit = dsp->digit_state.digits[0];
					*event_len = dsp->digit_state.digitlen[0] * 1000 / dsp->sample_rate;
				}
				memmove(&dsp->digit_state.digits[0], &dsp->digit_state.digits[1], dsp->digit_state.current_digits);
				memmove(&dsp->digit_state.digitlen[0], &dsp->digit_state.digitlen[1], dsp->digit_state.current_digits * sizeof(dsp->digit_state.digitlen[0]));
				dsp->digit_state.current_digits--;
				dsp->dtmf_began = 0;

				if (dsp->features & DSP_FEATURE_BUSY_DETECT) {
					/* Reset Busy Detector as we have some confirmed activity */
					memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
					memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));
					if(dspdebug) syslog(1, "DTMF Detected - Reset busydetector\n");
				}
			}

			if (event == AST_FRAME_DTMF_END) {
				if(dspdebug) syslog(LOG_DEBUG, "[%p] event[%u] digit[%c] len[%u]\n", dsp, event, *event_digit, *event_len);
				res |= DSP_PROCESS_RES_DTMF;
			}
		}
	}

	if (fax_digit) {
		/* Fax was detected - digit is either 'f' or 'e' */
		*event_digit = fax_digit;
		res |= DSP_PROCESS_RES_FAX; // fax
	}

	if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) {
		int _res_call_progress = __dsp_call_progress(dsp, shortdata, len);
		if (_res_call_progress) {
			switch (_res_call_progress) {
			case AST_CONTROL_ANSWER:
			case AST_CONTROL_BUSY:
			case AST_CONTROL_RINGING:
			case AST_CONTROL_CONGESTION:
			case AST_CONTROL_HANGUP:
				*res_call_progress = _res_call_progress;
				break;
			default:
				*res_call_progress = _res_call_progress;
				syslog(LOG_WARNING, "Don't know how to represent call progress message %d\n", res);
			}
			res |= DSP_PROCESS_RES_CALL_PROGRESSS;
		}
	}
	
	if ((dsp->features & DSP_FEATURE_WAITDIALTONE)) {
		int _res_call_progress = __dsp_call_progress(dsp, shortdata, len);
		if(_res_call_progress) {
			if(!*res_call_progress) {
				*res_call_progress = _res_call_progress;
			}
			res |= DSP_PROCESS_RES_WAITDIALTONE;
		}
	}
	
	return res;
}

static void dsp_prog_reset(struct dsp *dsp)
{
	int max = 0;
	unsigned int x;

	dsp->gsamp_size = modes[dsp->progmode].size;
	dsp->gsamps = 0;
	for (x = 0; x < ARRAY_LEN(modes[dsp->progmode].freqs); x++) {
		if (modes[dsp->progmode].freqs[x]) {
			goertzel_init(&dsp->freqs[x], (float)modes[dsp->progmode].freqs[x], dsp->sample_rate);
			max = x + 1;
		}
	}
	dsp->freqcount = max;
	dsp->ringtimeout = 0;
}

unsigned int dsp_get_sample_rate(const struct dsp *dsp)
{
	return dsp->sample_rate;
}

#if HEAPSAFE
	#define FILE_LINE(alloc_number) (__FILE__, __LINE__, alloc_number)
	void * operator new(size_t sizeOfObject, const char *memory_type1, int memory_type2 = 0, int alloc_number = 0);
#else
	#define FILE_LINE(alloc_number)
#endif

static struct dsp *__dsp_new(unsigned int sample_rate)
{
	extern int opt_silencethreshold;
	dsp *dsp_new = new FILE_LINE(3001) dsp;
	memset(dsp_new, 0, sizeof(dsp));

	if (dsp_new) {
		dsp_new->threshold = opt_silencethreshold;
		dsp_new->features = DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_FAX_DETECT | DSP_FEATURE_SILENCE_SUPPRESS;
		dsp_new->busycount = DSP_HISTORY;
		dsp_new->digitmode = DSP_DIGITMODE_DTMF;
		dsp_new->faxmode = DSP_FAXMODE_DETECT_ALL;
		dsp_new->sample_rate = sample_rate;
		/* Initialize digit detector */
		digit_detect_init(&dsp_new->digit_state, dsp_new->digitmode & DSP_DIGITMODE_MF, dsp_new->sample_rate);
		dsp_new->display_inband_dtmf_warning = 1;
		/* Initialize initial DSP progress detect parameters */
		dsp_prog_reset(dsp_new);
		/* Initialize fax detector */
		fax_detect_init(dsp_new);
	}
	return dsp_new;
}

struct dsp *dsp_new(void)
{
	return __dsp_new(DEFAULT_SAMPLE_RATE);
}

struct dsp *dsp_new_with_rate(unsigned int sample_rate)
{
	return __dsp_new(sample_rate);
}

void dsp_free(struct dsp *dsp)
{
	delete dsp;
}

void dsp_set_threshold(struct dsp *dsp, int threshold)
{
	dsp->threshold = threshold;
}

void dsp_set_busy_count(struct dsp *dsp, int cadences)
{
	if (cadences < 4) {
		cadences = 4;
	}
	if (cadences > DSP_HISTORY) {
		cadences = DSP_HISTORY;
	}
	dsp->busycount = cadences;
}

void dsp_set_busy_pattern(struct dsp *dsp, const struct dsp_busy_pattern *cadence)
{
	dsp->busy_cadence = *cadence;
	if(dspdebug) syslog(1, "dsp busy pattern set to %d,%d,%d,%d\n", cadence->pattern[0], cadence->pattern[1], (cadence->length == 4) ? cadence->pattern[2] : 0, (cadence->length == 4) ? cadence->pattern[3] : 0);
}

void dsp_digitreset(struct dsp *dsp)
{
	int i;

	dsp->dtmf_began = 0;
	if (dsp->digitmode & DSP_DIGITMODE_MF) {
		mf_detect_state_t *s = &dsp->digit_state.td.mf;
		/* Reinitialise the detector for the next block */
		for (i = 0; i < 6; i++) {
			goertzel_reset(&s->tone_out[i]);
		}
		s->hits[4] = s->hits[3] = s->hits[2] = s->hits[1] = s->hits[0] = 0;
		s->current_hit = 0;
		s->current_sample = 0;
	} else {
		dtmf_detect_state_t *s = &dsp->digit_state.td.dtmf;
		/* Reinitialise the detector for the next block */
		for (i = 0; i < 4; i++) {
			goertzel_reset(&s->row_out[i]);
			goertzel_reset(&s->col_out[i]);
		}
		s->lasthit = 0;
		s->current_hit = 0;
		s->energy = 0.0;
		s->current_sample = 0;
		s->hits = 0;
		s->misses = 0;
	}

	dsp->digit_state.digits[0] = '\0';
	dsp->digit_state.current_digits = 0;
}

void dsp_reset(struct dsp *dsp)
{
	int x;

	dsp->totalsilence = 0;
	dsp->gsamps = 0;
	for (x = 0; x < 4; x++) {
		dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
	}
	memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
	memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));
	dsp->ringtimeout = 0;
}

int dsp_set_digitmode(struct dsp *dsp, int digitmode)
{
	int new1;
	int old;

	old = dsp->digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX);
	new1 = digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX);
	if (old != new1) {
		/* Must initialize structures if switching from MF to DTMF or vice-versa */
		digit_detect_init(&dsp->digit_state, new1 & DSP_DIGITMODE_MF, dsp->sample_rate);
	}
	dsp->digitmode = digitmode;
	return 0;
}

int dsp_set_faxmode(struct dsp *dsp, int faxmode)
{
	if (dsp->faxmode != faxmode) {
		dsp->faxmode = faxmode;
		fax_detect_init(dsp);
	}
	return 0;
}

int dsp_set_call_progress_zone(struct dsp *dsp, char *zone)
{
	unsigned int x;

	for (x = 0; x < ARRAY_LEN(aliases); x++) {
		if (!strcasecmp(aliases[x].name, zone)) {
			dsp->progmode = aliases[x].mode;
			dsp_prog_reset(dsp);
			return 0;
		}
	}
	return -1;
}

int dsp_was_muted(struct dsp *dsp)
{
	return (dsp->mute_fragments > 0);
}

int dsp_get_tstate(struct dsp *dsp)
{
	return dsp->tstate;
}

int dsp_get_tcount(struct dsp *dsp)
{
	return dsp->tcount;
}

static int _dsp_init(int /*reload*/)
{
	thresholds[THRESHOLD_SILENCE] = DEFAULT_SILENCE_THRESHOLD;
	dtmf_normal_twist = DEF_DTMF_NORMAL_TWIST;
	dtmf_reverse_twist = DEF_DTMF_REVERSE_TWIST;
	relax_dtmf_normal_twist = DEF_RELAX_DTMF_NORMAL_TWIST;
	relax_dtmf_reverse_twist = DEF_RELAX_DTMF_REVERSE_TWIST;
        dtmf_hits_to_begin = DEF_DTMF_HITS_TO_BEGIN;
        dtmf_misses_to_end = DEF_DTMF_MISSES_TO_END;

#if 0
	if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
		return 0;
	}

	for (v = variable_browse(cfg, "default"); v; v = v->next) {
		if (!strcasecmp(v->name, "silencethreshold")) {
			if (sscanf(v->value, "%30d", &cfg_threshold) < 1) {
				syslog(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
			} else if (cfg_threshold < 0) {
				syslog(LOG_WARNING, "Invalid silence threshold '%d' specified, using default\n", cfg_threshold);
			} else {
				thresholds[THRESHOLD_SILENCE] = cfg_threshold;
			}
		} else if (!strcasecmp(v->name, "dtmf_normal_twist")) {
			if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
				syslog(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
			} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) {		/* < 3.0dB or > 20dB */
				syslog(LOG_WARNING, "Invalid dtmf_normal_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, dtmf_normal_twist);
			} else {
				dtmf_normal_twist = cfg_twist;
			}
		} else if (!strcasecmp(v->name, "dtmf_reverse_twist")) {
			if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
				syslog(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
			} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) {		/* < 3.0dB or > 20dB */
				syslog(LOG_WARNING, "Invalid dtmf_reverse_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, dtmf_reverse_twist);
			} else {
				dtmf_reverse_twist = cfg_twist;
			}
		} else if (!strcasecmp(v->name, "relax_dtmf_normal_twist")) {
			if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
				syslog(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
			} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) {		/* < 3.0dB or > 20dB */
				syslog(LOG_WARNING, "Invalid relax_dtmf_normal_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, relax_dtmf_normal_twist);
			} else {
				relax_dtmf_normal_twist = cfg_twist;
			}
		} else if (!strcasecmp(v->name, "relax_dtmf_reverse_twist")) {
			if (sscanf(v->value, "%30f", &cfg_twist) < 1) {
				syslog(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
			} else if ((cfg_twist < 2.0) || (cfg_twist > 100.0)) {		/* < 3.0dB or > 20dB */
				syslog(LOG_WARNING, "Invalid relax_dtmf_reverse_twist value '%.2f' specified, using default of %.2f\n", cfg_twist, relax_dtmf_reverse_twist);
			} else {
				relax_dtmf_reverse_twist = cfg_twist;
			}
		} else if (!strcasecmp(v->name, "dtmf_hits_to_begin")) {
			if (sscanf(v->value, "%30d", &cfg_threshold) < 1) {
				syslog(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
			} else if (cfg_threshold < 1) {		/* must be 1 or greater */
				syslog(LOG_WARNING, "Invalid dtmf_hits_to_begin value '%d' specified, using default of %d\n", cfg_threshold, dtmf_hits_to_begin);
			} else {
				dtmf_hits_to_begin = cfg_threshold;
			}
		} else if (!strcasecmp(v->name, "dtmf_misses_to_end")) {
			if (sscanf(v->value, "%30d", &cfg_threshold) < 1) {
				syslog(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
			} else if (cfg_threshold < 1) {		/* must be 1 or greater */
				syslog(LOG_WARNING, "Invalid dtmf_misses_to_end value '%d' specified, using default of %d\n", cfg_threshold, dtmf_misses_to_end);
			} else {
				dtmf_misses_to_end = cfg_threshold;
			}
		}
	}
	config_destroy(cfg);
#endif

	return 0;
}

int dsp_get_threshold_from_settings(enum threshold which)
{
	return thresholds[which];
}

int dsp_init(void)
{
	return _dsp_init(0);
}

int dsp_reload(void)
{
	return _dsp_init(1);
}
